import { ShaderMaterial, Vector2, ShaderChunk, NoBlending } from "three";

// 渲染目标绘制到渲染器着色器
export const baseTextureToRenderShader = new ShaderMaterial({
    uniforms: {
        tDiffuse: { value: null }, // 贴图纹理
        resolution: { value: new Vector2(1 / 1024, 1 / 1024) }
    },
    vertexShader: `
        ${ShaderChunk.common}
        ${ShaderChunk.logdepthbuf_pars_vertex}
        varying vec2 vUv;
        void main() {
            vUv = uv;
            gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
            ${ShaderChunk.logdepthbuf_vertex}
        }
    `,
    fragmentShader: `
        ${ShaderChunk.logdepthbuf_pars_fragment}
        precision highp float;

        uniform sampler2D tDiffuse;
    
        uniform vec2 resolution;
    
        varying vec2 vUv;
    
        #ifndef FXAA_DISCARD
                //
                // Only valid for PC OpenGL currently.
                // Probably will not work when FXAA_GREEN_AS_LUMA = 1.
                //
                // 1 = Use discard on pixels which don't need AA.
                //     For APIs which enable concurrent TEX+ROP from same surface.
                // 0 = Return unchanged color on pixels which don't need AA.
                //
                #define FXAA_DISCARD 0
        #endif
    
        /*--------------------------------------------------------------------------*/
        #define FxaaTexTop(t, p) texture2D(t, p, -100.0)
        #define FxaaTexOff(t, p, o, r) texture2D(t, p + (o * r), -100.0)
        /*--------------------------------------------------------------------------*/
    
        #define NUM_SAMPLES 5
    
        // assumes colors have premultipliedAlpha, so that the calculated color contrast is scaled by alpha
        float contrast( vec4 a, vec4 b ) {
                vec4 diff = abs( a - b );
                return max( max( max( diff.r, diff.g ), diff.b ), diff.a );
        }
    
        /*============================================================================
    
                                        FXAA3 QUALITY - PC
    
        ============================================================================*/
    
        /*--------------------------------------------------------------------------*/
        vec4 FxaaPixelShader(
                vec2 posM,
                sampler2D tex,
                vec2 fxaaQualityRcpFrame,
                float fxaaQualityEdgeThreshold,
                float fxaaQualityinvEdgeThreshold
        ) {
                vec4 rgbaM = FxaaTexTop(tex, posM);
                vec4 rgbaS = FxaaTexOff(tex, posM, vec2( 0.0, 1.0), fxaaQualityRcpFrame.xy);
                vec4 rgbaE = FxaaTexOff(tex, posM, vec2( 1.0, 0.0), fxaaQualityRcpFrame.xy);
                vec4 rgbaN = FxaaTexOff(tex, posM, vec2( 0.0,-1.0), fxaaQualityRcpFrame.xy);
                vec4 rgbaW = FxaaTexOff(tex, posM, vec2(-1.0, 0.0), fxaaQualityRcpFrame.xy);
                // . S .
                // W M E
                // . N .
    
                bool earlyExit = max( max( max(
                        contrast( rgbaM, rgbaN ),
                        contrast( rgbaM, rgbaS ) ),
                        contrast( rgbaM, rgbaE ) ),
                        contrast( rgbaM, rgbaW ) )
                        < fxaaQualityEdgeThreshold;
                // . 0 .
                // 0 0 0
                // . 0 .
    
                #if (FXAA_DISCARD == 1)
                        if(earlyExit) FxaaDiscard;
                #else
                        if(earlyExit) return rgbaM;
                #endif
    
                float contrastN = contrast( rgbaM, rgbaN );
                float contrastS = contrast( rgbaM, rgbaS );
                float contrastE = contrast( rgbaM, rgbaE );
                float contrastW = contrast( rgbaM, rgbaW );
    
                float relativeVContrast = ( contrastN + contrastS ) - ( contrastE + contrastW );
                relativeVContrast *= fxaaQualityinvEdgeThreshold;
    
                bool horzSpan = relativeVContrast > 0.;
                // . 1 .
                // 0 0 0
                // . 1 .
    
                // 45 deg edge detection and corners of objects, aka V/H contrast is too similar
                if( abs( relativeVContrast ) < .3 ) {
                        // locate the edge
                        vec2 dirToEdge;
                        dirToEdge.x = contrastE > contrastW ? 1. : -1.;
                        dirToEdge.y = contrastS > contrastN ? 1. : -1.;
                        // . 2 .      . 1 .
                        // 1 0 2  ~=  0 0 1
                        // . 1 .      . 0 .
    
                        // tap 2 pixels and see which ones are "outside" the edge, to
                        // determine if the edge is vertical or horizontal
    
                        vec4 rgbaAlongH = FxaaTexOff(tex, posM, vec2( dirToEdge.x, -dirToEdge.y ), fxaaQualityRcpFrame.xy);
                        float matchAlongH = contrast( rgbaM, rgbaAlongH );
                        // . 1 .
                        // 0 0 1
                        // . 0 H
    
                        vec4 rgbaAlongV = FxaaTexOff(tex, posM, vec2( -dirToEdge.x, dirToEdge.y ), fxaaQualityRcpFrame.xy);
                        float matchAlongV = contrast( rgbaM, rgbaAlongV );
                        // V 1 .
                        // 0 0 1
                        // . 0 .
    
                        relativeVContrast = matchAlongV - matchAlongH;
                        relativeVContrast *= fxaaQualityinvEdgeThreshold;
    
                        if( abs( relativeVContrast ) < .3 ) { // 45 deg edge
                                // 1 1 .
                                // 0 0 1
                                // . 0 1
    
                                // do a simple blur
                                return mix(
                                        rgbaM,
                                        (rgbaN + rgbaS + rgbaE + rgbaW) * .25,
                                        .4
                                );
                        }
    
                        horzSpan = relativeVContrast > 0.;
                }
    
                if(!horzSpan) rgbaN = rgbaW;
                if(!horzSpan) rgbaS = rgbaE;
                // . 0 .      1
                // 1 0 1  ->  0
                // . 0 .      1
    
                bool pairN = contrast( rgbaM, rgbaN ) > contrast( rgbaM, rgbaS );
                if(!pairN) rgbaN = rgbaS;
    
                vec2 offNP;
                offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x;
                offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y;
    
                bool doneN = false;
                bool doneP = false;
    
                float nDist = 0.;
                float pDist = 0.;
    
                vec2 posN = posM;
                vec2 posP = posM;
    
                int iterationsUsed = 0;
                int iterationsUsedN = 0;
                int iterationsUsedP = 0;
                for( int i = 0; i < NUM_SAMPLES; i++ ) {
                        iterationsUsed = i;
    
                        float increment = float(i + 1);
    
                        if(!doneN) {
                                nDist += increment;
                                posN = posM + offNP * nDist;
                                vec4 rgbaEndN = FxaaTexTop(tex, posN.xy);
                                doneN = contrast( rgbaEndN, rgbaM ) > contrast( rgbaEndN, rgbaN );
                                iterationsUsedN = i;
                        }
    
                        if(!doneP) {
                                pDist += increment;
                                posP = posM - offNP * pDist;
                                vec4 rgbaEndP = FxaaTexTop(tex, posP.xy);
                                doneP = contrast( rgbaEndP, rgbaM ) > contrast( rgbaEndP, rgbaN );
                                iterationsUsedP = i;
                        }
    
                        if(doneN || doneP) break;
                }
    
    
                if ( !doneP && !doneN ) return rgbaM; // failed to find end of edge
    
                float dist = min(
                        doneN ? float( iterationsUsedN ) / float( NUM_SAMPLES - 1 ) : 1.,
                        doneP ? float( iterationsUsedP ) / float( NUM_SAMPLES - 1 ) : 1.
                );
    
                // hacky way of reduces blurriness of mostly diagonal edges
                // but reduces AA quality
                dist = pow(dist, .5);
    
                dist = 1. - dist;
    
                return mix(
                        rgbaM,
                        rgbaN,
                        dist * .5
                );
        }
    
        void main() {
                const float edgeDetectionQuality = .2;
                const float invEdgeDetectionQuality = 1. / edgeDetectionQuality;
    
                gl_FragColor = sRGBTransferOETF(FxaaPixelShader(
                        vUv,
                        tDiffuse,
                        resolution,
                        edgeDetectionQuality, // [0,1] contrast needed, otherwise early discard
                        invEdgeDetectionQuality
                ));
            ${ShaderChunk.logdepthbuf_fragment}
        }
    `,
    blending: NoBlending, // 渲染时不与背景融合计算 直接替换
    depthTest: false,
    depthWrite: false,
    transparent: true
});
